const std = @import("std");
const awtk = @import("awtk");
const object = @import("../object.zig");
const mvvm = @import("../mvvm.zig");
const user_model = @import("../models/user.zig");

const c = awtk.c;

pub const UserVM = struct {
    _emitter: awtk.Emitter = undefined,
    _req: ?*mvvm.NavigatorRequest = null,
    name: awtk.String = undefined,
    age: u8 = 0,
    gender: user_model.Gender = .Male,
    user: *user_model.User = undefined,
    owned: bool = false,

    const Self = @This();

    pub fn init(self: *Self) !void {
        std.log.debug("{s}.{s}", .{ awtk.baseTypeName(@This()), @src().fn_name });
        _ = c.emitter_init(&self._emitter);
        self.name = awtk.String.init(8);
        self.user = try awtk.gallocator.create(user_model.User);
        self.user.init();
        self.owned = true;
    }

    pub fn initWithReq(self: *Self, creq: ?*mvvm.NavigatorRequest) !void {
        std.log.debug("{s}.{s}", .{ awtk.baseTypeName(@This()), @src().fn_name });
        self.name = awtk.String.init(8);
        if (creq) |req| {
            _ = c.emitter_init(&self._emitter);
            self._req = req;
            const user_ptr = c.tk_object_get_prop_pointer(c.OBJECT(req), awtk.baseTypeName(user_model.User)) orelse return error.RequestError;
            self.user = @ptrCast(@alignCast(user_ptr));
            self.owned = false;

            try self.name.setStr(self.user.name.str());
            self.age = self.user.age;
            self.gender = self.user.gender;
        } else {
            try self.init();
        }
    }

    pub fn deinit(self: *Self) void {
        _ = c.emitter_deinit(&self._emitter);
        self.name.deinit();
        self._req = null;
        if (self.owned) {
            self.user.deinit();
            awtk.gallocator.destroy(self.user);
        }
    }

    pub fn emitter(self: *Self) *awtk.Emitter {
        return &self._emitter;
    }

    // 虚拟property: isAdult
    pub fn getIsAdult(self: Self, _: []const u8, v: *awtk.Value) c.ret_t {
        _ = c.value_set_bool(v, user_model.User.isAdult(self.age));
        return c.RET_OK;
    }

    pub fn addAge(self: *Self) !awtk.Notify {
        self.age += 1;
        return .PropertyChanged;
    }

    pub fn canAddAge(self: Self) bool {
        return self.age < 5;
    }

    pub fn subAge(self: *Self) !awtk.Notify {
        self.age -= 1;
        return .PropertyChanged;
    }

    pub fn canSubAge(self: Self) bool {
        return self.age > 0;
    }

    pub fn apply(self: *Self) !awtk.Notify {
        try self.user.setName(self.name.str());
        self.user.age = self.age;
        self.user.gender = self.gender;

        std.log.debug("user changed: {f}", .{self.user});
        if (self._req) |req| {
            var value: awtk.Value = undefined;
            _ = c.value_set_uint32(&value, c.EVT_PROPS_CHANGED);
            defer _ = c.value_reset(&value);

            _ = c.navigator_request_on_result(req, &value);
        }

        return .Ok;
    }

    pub fn canApply(self: Self) bool {
        const user = self.user;
        return self.name.len() > 0 and
            (!user.name.eql(self.name.str()) or
                self.age != user.age or
                self.gender != user.gender);
    }
};
